#pragma once

#include "headers.hpp"

namespace alpha { namespace http {

namespace detail {

template <typename AsyncReadStream, typename Buffer, typename HTTP_Response, typename ReadHandler>
class async_read_response_handler
{
public:
	async_read_response_handler(AsyncReadStream &stream, Buffer &streambuf, HTTP_Response &response, ReadHandler handler)
		: stream_(stream), streambuf_(streambuf), response_(response), handler_(handler)
	{}
	void operator()(const boost::system::error_code& ec, std::size_t bytes_transferred)
	{
		// Check for errors.
		if (ec)
		{
			handler_(ec, bytes_transferred);
			return;
		}
		// read the status line
		std::istream ss(&streambuf_);
		char buf[5];
		ss.read(buf, 5);
		std::getline(ss, response_.version, ' ');
		std::getline(ss, response_.status, ' ');
		gethttpline(ss, response_.reason);
		if (memcmp(buf, "HTTP/", 5))
		{
			handler_(boost::system::error_code(EILSEQ, boost::system::errno_ecat), bytes_transferred);
			return;
		}
		async_read_headers(stream_, streambuf_, response_.headers, handler_);
	}
//private:
	AsyncReadStream &stream_;
	Buffer &streambuf_;
	HTTP_Response &response_;
	ReadHandler handler_;
};

template <typename AsyncReadStream, typename Buffer, typename HTTP_Response, typename ReadHandler>
inline void* asio_handler_allocate(std::size_t size, async_read_response_handler<AsyncReadStream, Buffer, HTTP_Response, ReadHandler>* this_handler)
{
	return boost_asio_handler_alloc_helpers::allocate(size, &this_handler->handler_);
}

template <typename AsyncReadStream, typename Buffer, typename HTTP_Response, typename ReadHandler>
inline void asio_handler_deallocate(void* pointer, std::size_t size, async_read_response_handler<AsyncReadStream, Buffer, HTTP_Response, ReadHandler>* this_handler)
{
	boost_asio_handler_alloc_helpers::deallocate(pointer, size, &this_handler->handler_);
}

template <typename Function, typename AsyncReadStream, typename Buffer, typename HTTP_Response, typename ReadHandler>
inline void asio_handler_invoke(const Function& function, async_read_response_handler<AsyncReadStream, Buffer, HTTP_Response, ReadHandler>* this_handler)
{
	asio_handler_invoke_helpers::invoke(function, &this_handler->handler_);
}

} // namespace detail

template <typename Async_Read_Stream, typename Buffer, typename HTTP_Response, typename Handler>
void async_read_response(Async_Read_Stream &s, Buffer &b, HTTP_Response &r, Handler handler)
{
	// should be "\r\n"
	boost::asio::async_read_until(s, b, '\n', detail::async_read_response_handler<Async_Read_Stream, Buffer, HTTP_Response, Handler>(s, b, r, handler));
}

template <typename Buffer, typename HTTP_Response>
void write_response(Buffer &b, HTTP_Response &r)
{
	std::ostream ss(&b);
	ss << "HTTP/" << r.version << ' ' << r.status << ' ' << r.reason << "\r\n";
	write_headers(b, r.headers);
}

template <typename S = std::string, typename H = std::map<S, S> >
struct basic_response
{
	typedef S string_type;
	typedef H headers_type;
	string_type version;
	string_type status;
	string_type reason;
	headers_type headers;
};

typedef basic_response<> response;

}} // namespace alpha::http 
